#!/bin/sh

if [ "$1" = "stop" ]
then
    # umount all network configs
    # don't just remount the one of the config in case the config changed
    umount -l -a -t nfs,nfs2,nfs4
    umount -l -a -t cifs
    exit 0
fi

if [ "$1" != "start" ]
then
    exit 0
fi

printf "Starting share: "

###
# SHARECONFFILE
# can contain :
# INTERNAL     => /dev/mmcblk0p2
# RAM          => tmpfs
# ANYEXTERNAL  => any device found not starting by /dev/mmcblk0p (use it when you've several usb keys, but plug only one at a time)
# NETWORK      => use the NAS specified by sharenetwork_[smb-or-nfs][0-9] options
# DEV [FSUUID] => a device having the FSUID uuid
# DEVICES      => use local storage devices specified by sharedevice_part[1-9]
###

SHARECONFFILE="/boot/batocera-boot.conf"
INTERNALDEVICE=$(batocera-part "share_internal")
INTERNALDEVICETYPE=$(blkid "${INTERNALDEVICE}" | sed -e s+'^.* TYPE="\([^"]*\)\".*'+'\1'+)

SHAREDEVICE=$(cat "${SHARECONFFILE}" | grep -E '^[ ]*sharedevice=' | head -n1 | cut -d'=' -f2)
[ "$?" -ne "0" -o "$SHAREDEVICE" = "" ] && SHAREDEVICE=INTERNAL

getMaxTryConfig() {
    SHARECONFFILE=${1}

    X=$(grep -E '^[ \t]*sharewait[ \t]*=' "${SHARECONFFILE}" | sed -e s+'^[ \t]*sharewait[ \t]*='+''+)
    if echo "${X}" | grep -qE '^[0-9][0-9]*$'
    then
        echo "${X}"
        return
    fi
    echo 15 # default value
}

MAXTRY=$(getMaxTryConfig "${SHARECONFFILE}")
NTRY=0

mountRAMDisk() {
    mount -t tmpfs -o size=256M tmpfs /userdata
}

mountDeviceOrFallback() {
    DEVICE=$1
    TDEVICE=$2
    BATOCERAFULLFS="/var/batocerafs"
    FALLBACK=1

    if [ -n "${DEVICE}" ]
    then
        if mkdir -p "${BATOCERAFULLFS}"
        then
            if batocera-mount "${TDEVICE}" 1 "${DEVICE}" "${BATOCERAFULLFS}"
            then
                if mkdir -p "${BATOCERAFULLFS}/batocera"
                then
                    if mount --bind "${BATOCERAFULLFS}/batocera" "/userdata" -o "noatime"
                    then
                        FALLBACK=0
                    fi
                fi
            fi
        fi
    fi

    if [ "${FALLBACK}" = "1" ]
    then
        if ! batocera-mount "${INTERNALDEVICETYPE}" 1 "${INTERNALDEVICE}" /userdata
        then
            mountRAMDisk
        fi
    fi
}

mountPartByTag() {
    TAG=$1
    DIR=$2

    FSTYPE="$(blkid -l -t "${TAG}" -s TYPE -o value)" || return 1

    case "${FSTYPE}" in
        "ntfs")
            # Force use of the FUSE ntfs-3g driver
            FSTYPE=ntfs-3g
            ;;
    esac

    mount -t "${FSTYPE}" "${TAG}" "${DIR}"
}

mountPartSubdir() {
    CMD_KEYPART=$1
    CMD_PART=$2
    CMD_SUBDIR=$3
    CMD_TDIR=$4
    mkdir -p "/var/batocera_subdir_${CMD_KEYPART}" || return 1
    mountPartByTag "UUID=${CMD_PART}" "/var/batocera_subdir_${CMD_KEYPART}" || return 1
    mkdir -p "/var/batocera_subdir_${CMD_KEYPART}/${CMD_SUBDIR}" || return 1
    mount --bind "/var/batocera_subdir_${CMD_KEYPART}/${CMD_SUBDIR}" "${CMD_TDIR}" || return 1
    return 0
}

mountDevicesOrNetwork() {
    # /boot/batocera-boot.conf examples :
    # sharedevice=NETWORK
    #
    # Basic commands : sharenetwork_<nfs|smb><[0-9]>=<DIR>@<host>:<remote directory>:<mount options>
    # Where <DIR> can be one of SHARE|ROMS|SAVES|BIOS|MUSIC|DECORATIONS|SCREENSHOTS|THEMES|CHEATS|SOUNDS|LIBRARY|SPLASH|CONFIGS
    #
    # sharenetwork_nfs1=SHARE@192.168.0.1:/Documents/batocera
    # or
    # sharenetwork_nfs1=ROMS@192.168.0.1:/Documents/batocera/roms
    # sharenetwork_nfs2=SAVES@192.168.0.1:/Documents/batocera/saves
    # or
    # sharenetwork_smb1=SHARE@192.168.0.1:Documents/batocera:guest
    #
    # Advanced commands : sharenetwork_cmd<[0-9]>=<command to run>
    # sharenetwork_cmd1=mount -o port=2049,nolock,proto=tcp 192.168.0.1:/Documents/batocera /userdata
    # or
    # sharenetwork_cmd1=mount -o port=2049,nolock,proto=tcp 192.168.0.1:/Documents/batocera/roms /userdata/roms
    # sharenetwork_cmd2=mount -o port=2049,nolock,proto=tcp 192.168.0.1:/Documents/batocera/saves /userdata/saves
    # or
    # sharenetwork_cmd1=mount.cifs //192.168.0.1/batocera /userdata -o guest
    #
    # mounting full partitions or sub folders of partitions on top of the internal partition
    # sharedevice=DEVICES
    # sharedevice_part1=SHARE@abcdefg-1234-5678-hijk-lmnopq0000
    # sharedevice_part2=ROMS@68dc61b8-5008-4659-bde9-5938a547680a:/Documents/roms

    # execute all commands in /boot/batocera-boot.conf which are like : sharenetwork_cmd1=my command
    if ! grep -E '^[ ]*share(network|device)_[a-z]*[0-9][ ]*=' "${SHARECONFFILE}" |
         sed -e s+'^[ ]*share\(network\|device\)_\([a-z]*\)[0-9][ ]*='+'\2 '+ |
        while read -r CTYPE CMD
        do
            XWAIT=4 # N seconds between each try
            XTRY=$((MAXTRY / XWAIT)) # X tries and give up
            
            while [ "${XTRY}" -gt 0 ]
            do
                XTRY=$((XTRY-1))
                CMD_EXEC=echo
                if [ "${CTYPE}" = "cmd" ]
                then
                    CMD_EXEC="${CMD}"
                else
		    CMD_TARGET=$(echo "${CMD}" | sed -e s+'^\([^@]*\)@.*$'+'\1'+)
		    case "${CTYPE}" in
			"nfs"|"smb")
			    CMD_HOST=$(echo "${CMD}" | sed -e s+'^[^@]*@\([^:]*\):.*$'+'\1'+)
			    CMD_RDIR=$(echo "${CMD}" | sed -e s+'^[^@]*@[^:]*:\([^:]*\).*$'+'\1'+)
			    CMD_OPT=$(echo "${CMD}" | sed -e s+'^[^@]*@[^:]*:[^:]*'+''+ -e s+'^:'++)
			    ;;
			"part")
			    CMD_PART=$(echo "${CMD}" | sed -e s+'^[^@]*@\(.*\)$'+'\1'+)
			    CMD_SUBDIR="/"
			    CMD_KEYPART=$(echo "${CMD_PART}" | md5sum | cut -c 1-10)
			    if echo "${CMD_PART}" | grep -qE ':'
			    then
				CMD_SUBDIR=$(echo "${CMD_PART}" | sed -e s+"^[^:]*:"++)
				CMD_PART=$(echo "${CMD_PART}" | sed -e s+":.*$"++)
			    fi
			    ;;
			*)
			    echo "unknown type" >&2
			    return 1
		    esac

                    # MAP to the batocera directory
                    CMD_TDIR="/userdata"
                    case "${CMD_TARGET}" in
                        "SHARE")
                            CMD_TDIR="/userdata"
                        ;;
                        "ROMS")
                            CMD_TDIR="/userdata/roms"
                        ;;
                        "SAVES")
                            CMD_TDIR="/userdata/saves"
                        ;;
                        "BIOS")
                            CMD_TDIR="/userdata/bios"
                        ;;
                        "MUSIC")
                            CMD_TDIR="/userdata/music"
                        ;;
                        "DECORATIONS")
                            CMD_TDIR="/userdata/decorations"
                        ;;
                        "SCREENSHOTS")
                            CMD_TDIR="/userdata/screenshots"
                        ;;
                        "THEMES")
                            CMD_TDIR="/userdata/themes"
                        ;;
                        "CHEATS")
                            CMD_TDIR="/userdata/cheats"
                        ;;
                        "SOUNDS")
                            CMD_TDIR="/userdata/sounds"
                        ;;
                        "LIBRARY")
                            CMD_TDIR="/userdata/library"
                        ;;
                        "SPLASH")
                            CMD_TDIR="/userdata/splash"
                        ;;
                        "CONFIGS")
                            CMD_TDIR="/userdata/system/configs"
                        ;;
                        "RECORDINGS")
                            CMD_TDIR="/userdata/recordings"
                        ;;
                    esac

                    mkdir -p "${CMD_TDIR}" || return 1

                    case "${CTYPE}" in
                        "nfs")
                            CMD_ADDOPT=
                            [ -n "${CMD_OPT}" ] && CMD_ADDOPT=",${CMD_OPT}"
                            CMD_EXEC="mount -o port=2049,nolock,proto=tcp${CMD_ADDOPT} ${CMD_HOST}:${CMD_RDIR} ${CMD_TDIR}"
                            ;;
                        "smb")
                            CMD_ADDOPT=
                            [ -n "${CMD_OPT}" ] && CMD_ADDOPT="-o ${CMD_OPT}"
                            CMD_EXEC="mount.cifs //${CMD_HOST}/${CMD_RDIR} ${CMD_TDIR} ${CMD_ADDOPT}"
                            ;;
			"part")
			    if test "${CMD_SUBDIR}" = "/"
			    then
				CMD_EXEC="mountPartByTag UUID=${CMD_PART} ${CMD_TDIR}"
			    else
				CMD_EXEC="mountPartSubdir ${CMD_KEYPART} ${CMD_PART} ${CMD_SUBDIR} ${CMD_TDIR}"
			    fi
			    ;;
                    esac
                fi

                echo "${CMD_EXEC}"
                if eval ${CMD_EXEC}
                then
                    echo "success"
                    XTRY=0
                else
                    echo "fail (${XTRY} : ${CMD_EXEC})"
                    # give up
                    if [ ${XTRY} -eq 0 ]
                    then
                        echo "giving up"
                        return 1
                    fi
                    sleep ${XWAIT} # wait n seconds between each try
                fi
            done
        done
    then
        return 1
    fi
    return 0
}

prepareLUKS() {
    waitnet_tries=$(seq "$MAXTRY")
    # Find encrypted block devices
    for dev in $(blkid -t TYPE="crypto_LUKS" -o device)
    do
        # Wait for network to be up, in case any of our LUKS devices are
        # bound to a networked tang server via clevis for Network-Based
        # Disk Encryption (NBDE)
        for waitnet in waitnet_tries
        do
            if [ -n "$(ip route)" ]
            then
                break
            fi
            echo "Waiting for network..."
            sleep 1
        done
        unset waitnet_tries
        # Try to unlock the device automatically via TPM2 or tang, per
        # the volume's bound policies
        if ! clevis luks unlock -d "${dev}"
        then
            # If unable to unlock automatically, switch to the console
            # TTY and open manually via passphrase prompt
            TTY=1
            read -r cmdline < /proc/cmdline
            for param in ${cmdline}
            do
                case ${param} in
                    console=tty*) TTY=${param#console=tty};;
                    *)            continue;;
                esac
            done
            chvt "${TTY}"
            cryptsetup open "${dev}" "$(cryptsetup luksUUID ${dev})"
        fi
    done
    # probe any partitions found (full disk encryption)
    dmsetup ls --target crypt --exec partprobe
}

# call user script (for nas wakeonlan for example)
test -e /boot/preshare.sh && bash /boot/preshare.sh start

RMODE="$SHAREDEVICE"

if echo "${RMODE}" | grep -qE '^DEV '
then
    MODE="DEV"
    UUID=$(echo "${RMODE}" | sed -e s+'^DEV '++)
else
    MODE=${RMODE}
fi

VGSCAN=/usr/sbin/vgscan
VGCHANGE=/usr/sbin/vgchange

if [ -x ${VGSCAN} -a -x ${VGCHANGE} ]
then
    ${VGSCAN}
    ${VGCHANGE} -ay
fi

luks_enabled="$(/usr/bin/batocera-settings-get -f "$SHARECONFFILE" luks.enabled)"
if [ "${luks_enabled}" = "1" -o -z "${luks_enabled}" ]
then
    prepareLUKS
fi

case "${MODE}" in
    "DEV")
        LDEVICE=$(blkid | grep " UUID=\"${UUID}\"" | head -1)
        while [ -z "${LDEVICE}" -a "${NTRY}" -lt "${MAXTRY}" ] # wait the device that can take some seconds after udev started
        do
            NTRY=$((NTRY+1))
            sleep 1
            LDEVICE=$(blkid | grep " UUID=\"${UUID}\"" | head -1)
        done
        DEVICE=$(echo "${LDEVICE}" | sed -e s+'^\([^:]*\):.*$'+'\1'+)
        TDEVICE=$(echo "${LDEVICE}" | sed -e s+'^.* TYPE="\([^"]*\)".*$'+'\1'+)
        mountDeviceOrFallback "${DEVICE}" "${TDEVICE}"
    ;;
    "ANYEXTERNAL")
        PARTPREFIX=$(batocera-part prefix "${INTERNALDEVICE}")
        LDEVICE=$(blkid | grep -F TYPE= | grep -F UUID= | grep -vE "^${PARTPREFIX}" | sort | head -1)
        while [ -z "${LDEVICE}" ] && [ "${NTRY}" -lt "${MAXTRY}" ] # wait the device that can take some seconds after udev started
        do
            NTRY=$((NTRY+1))
            sleep 1
            LDEVICE=$(blkid | grep -vE "^${PARTPREFIX}" | head -1)
        done
        DEVICE=$(echo "${LDEVICE}" | sed -e s+'^\([^:]*\):.*$'+'\1'+)
        TDEVICE=$(echo "${LDEVICE}" | sed -e s+'^.* TYPE="\([^"]*\)".*$'+'\1'+)
        mountDeviceOrFallback "${DEVICE}" "${TDEVICE}"
    ;;
    "RAM")
        mountRAMDisk
    ;;
    "NETWORK"|"DEVICES")
        # first, INTERNAL mount, then, network mount over the NETWORK mounts
        # to allow to mount over /userdata, but only over /userdata/roms if wanted
        # mounting network mounts over usb key have not really sense
        if ! batocera-mount "${INTERNALDEVICETYPE}" 1 "${INTERNALDEVICE}" /userdata
        then
            # fallback
            mountRAMDisk
        fi

        # Network mounts
        # no fallback required, mounted on the share
        mountDevicesOrNetwork > /tmp/mountDevicesOrNetwork.log 2> /tmp/mountDevicesOrNetwork.err # could be usefull to debug
    ;;
    "INTERNAL"|*)
        if ! batocera-mount "${INTERNALDEVICETYPE}" 1 "${INTERNALDEVICE}" /userdata
        then
            # fallback
            # the internal partition is no more required in fact
           mountRAMDisk
        fi
    ;;
esac

# filesystem compression
compressenabled="$(/usr/bin/batocera-settings-get system.fscompression.enabled)"
if [ "$compressenabled" = "1" ]
then
    if grep -qE "^/dev/[^ ]* /userdata btrfs.*$" /proc/mounts
    then
        # compress-force used here to have the zstd-compressor check the total file size instead of btrfs before committing to compressing
        # btrfs's algorithm can be innaccurate when compared with zstd-compressor
        mount -o remount,compress-force=zstd,autodefrag /userdata || exit 1
    fi
fi

# some custom files, like extension_strace.tar.gz or gdb.
find /userdata/system -maxdepth 1 -name "extension_*.tar.gz" |
    while read FILE
    do
	(cd / && gunzip -c "${FILE}" | tar xf -)
    done

# now, let mount delayed usbmount devices
ls /var/run/usbmount.delay |
    while read -r RULE
    do
        RTYPE=$(echo "${RULE}" | sed -e s+'^[0-9]*\.'++)
        # source the udev context and apply the usbmount
        (. "/var/run/usbmount.delay/${RULE}"
        /usr/share/usbmount/usbmount "${RTYPE}"
        rm "/var/run/usbmount.delay/${RULE}")
    done
touch /var/run/batocera.share.mounted # reenable standard usbmount

echo "done."
